1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.math.matrix;
12 import core.math;
13 
14 enum MatrixType
15 {
16     COLUMN_MAJOR,
17     ROW_MAJOR
18 }
19 
20 struct Matrix3
21 {
22     float[9] values;
23     pragma(inline) inout ref auto opIndex(size_t i) return
24     {
25         return values[i];
26     }
27     pragma(inline) inout ref auto opIndex(size_t i, size_t j) return
28     {
29         return values[i*3+j];
30     }
31     pragma(inline, true)
32     static Matrix3 translation(float x, float y)
33     {
34         return Matrix3([
35             1, 0, 0,
36             0, 1, 0,
37             x, y, 1
38         ]);
39     }
40     static Matrix3 identity()
41     {
42         return Matrix3([
43             1,0,0,
44             0,1,0,
45             0,0,1
46         ]);
47     }
48     Matrix3 translate(float x, float y)
49     {
50         return this * translation(x,y);
51     }
52 
53     Matrix3 scale(float x, float y)
54     {
55         return this * Matrix3([
56             x, 0, 0,
57             0, y, 0,
58             0, 0, 1
59         ]);
60     }
61     Matrix3 transpose()
62     {
63         return Matrix3([
64             this[0], this[3], this[6],
65             this[1], this[4], this[7],
66             this[2], this[5], this[8]
67         ]);
68     }
69 
70     static Matrix3 rotation(float radians)
71     {
72         float c = cos(radians);
73         float s = sin(radians);
74 
75         return Matrix3([
76             c, s, 0,
77            -s, c, 0,
78             0, 0, 1
79         ]);
80     }
81 
82     Matrix3 rotate(float radians)
83     {
84         return this * rotation(radians);
85     }
86 
87 
88     Matrix3 opBinary(string op, R)(const R rhs) const
89     {
90         Matrix3 ret;
91         ret.values = this.values;
92         static if(op == "*")
93         {
94             static if(is(R == float))
95             {
96                 ret[]*= rhs;
97             }
98             else //Matrix case
99             {
100                 for(uint i = 0; i < 9; i+=3)
101                 {
102                     ret[i] = values[i]*rhs[0] +values[i+1]*rhs[3] + values[i+2]*rhs[6];
103                     ret[i+1] = values[i]*rhs[1] +values[i+1]*rhs[4] + values[i+2]*rhs[7];
104                     ret[i+2] = values[i]*rhs[2] +values[i+1]*rhs[5] + values[i+2]*rhs[8];
105                 }
106             }
107         }
108         else static if(op == "+")
109         {
110             foreach(i, v; values)
111                 ret[i]+=rhs[i];
112         }
113         else static if(op == "-")
114         {
115             foreach(i, v; values)
116                 ret[i]-=rhs[i];
117         }
118         else static if(op == "/")
119         {
120             static if(is(R == float))
121             {
122                 ret[]/=rhs;
123             }
124         }
125         return ret;
126     }
127 
128     T opCast(T)() const
129     {
130         static assert(is(T == float[9]), "Matrix3 can only be cast to float[9]");
131         return values;
132     }
133 
134     alias values this;
135 }
136 
137 struct Matrix4
138 {
139     float[16] values;
140 
141     static Matrix4 identity()
142     {
143         return Matrix4([
144             1.0, 0, 0, 0,
145             0, 1.0, 0, 0,
146             0, 0, 1.0, 0,
147             0, 0, 0, 1.0
148         ]);
149     }
150     pragma(inline) inout ref auto opIndex(size_t i) return
151     {
152         return values[i];
153     }
154     pragma(inline) inout ref auto opIndex(size_t i, size_t j) return
155     {
156         return values[i*4+j];
157     }
158     pragma(inline, true)
159     static Matrix4 translation(float x, float y, float z)
160     {
161         return Matrix4([
162             1, 0, 0, 0,
163             0, 1, 0, 0,
164             0, 0, 1, 0,
165             x, y, z, 1
166         ]);
167     }
168     Matrix4 translate(float x, float y, float z)
169     {
170         return this * translation(x,y,z);
171     }
172 
173     static Matrix4 createScale(float x, float y, float z)
174     {
175         return Matrix4([
176             x, 0, 0, 0,
177             0, y, 0, 0,
178             0, 0, z, 0,
179             0, 0, 0, 1
180         ]);
181     }
182 
183     Matrix4 scale(float x, float y, float z)
184     {
185         return this * Matrix4([
186             x, 0, 0, 0,
187             0, y, 0, 0,
188             0, 0, z, 0,
189             0, 0, 0, 1
190         ]);
191     }
192     Matrix4 transpose()
193     {
194         return Matrix4([
195             this[0], this[4], this[8],  this[12],
196             this[1], this[5], this[9],  this[13],
197             this[2], this[6], this[10], this[14],
198             this[3], this[7], this[11], this[15]
199         ]);
200     }
201 
202     static Matrix4 rotationX(float radians)
203     {
204         float c = cos(radians);
205         float s = sin(radians);
206 
207         return Matrix4([
208             1, 0, 0, 0,
209             0, c, s, 0,
210             0,-s, c, 0,
211             0, 0, 0, 1
212         ]);
213     }
214 
215     static Matrix4 rotationY(float radians)
216     {
217         float c = cos(radians);
218         float s = sin(radians);
219 
220         return Matrix4([
221             c, 0, s, 0,
222             0, 1, 0, 0,
223            -s, 0, c, 0,
224             0, 0, 0, 1
225         ]);
226     }
227     static Matrix4 rotationZ(float radians)
228     {
229         float c = cos(radians);
230         float s = sin(radians);
231 
232         return Matrix4([
233             c, s, 0, 0,
234            -s, c, 0, 0,
235             0, 0, 1, 0,
236             0, 0, 0, 1
237         ]);
238     }
239 
240     Matrix4 rotate(float x_angle, float y_angle, float z_angle)
241     {
242         return this * rotationX(x_angle) * rotationY(y_angle) * rotationZ(z_angle);
243     }
244 
245 
246     Matrix4 opBinary(string op, R)(const R rhs) const
247     {
248         Matrix4 ret;
249         ret.values = this.values;
250         static if(op == "*")
251         {
252             static if(is(R == float))
253             {
254                 ret[]*= rhs;
255             }
256             else //Matrix case
257             {
258                 for(uint i = 0; i < 16; i+=4)
259                 {
260                     ret[i]   = values[i]*rhs[0] +values[i+1]*rhs[4] + values[i+2]*rhs[8] + values[i+3]*rhs[12];
261                     ret[i+1] = values[i]*rhs[1] +values[i+1]*rhs[5] + values[i+2]*rhs[9] + values[i+3]*rhs[13];
262                     ret[i+2] = values[i]*rhs[2] +values[i+1]*rhs[6] + values[i+2]*rhs[10] + values[i+3]*rhs[14];
263                     ret[i+3] = values[i]*rhs[3] +values[i+1]*rhs[7] + values[i+2]*rhs[11] + values[i+3]*rhs[15];
264                 }
265             }
266         }
267         else static if(op == "+")
268         {
269             foreach(i, v; values)
270                 ret[i]+=rhs[i];
271         }
272         else static if(op == "-")
273         {
274             foreach(i, v; values)
275                 ret[i]-=rhs[i];
276         }
277         else static if(op == "/")
278         {
279             static assert(is(R == float), "Only float is valid for matrix division");
280             ret[]/=rhs;
281         }
282         return ret;
283     }
284 
285     /**
286     * Based on the document on MSDN:
287     * https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixorthooffcenterlh?redirectedfrom=MSDN
288     */
289     static Matrix4 orthoLH(float left, float right, float bottom, float top, float znear, float zfar)
290     {
291         
292         return Matrix4([
293             2/(right-left), 0, 0, 0,
294             0, 2/(top-bottom), 0, 0,
295             0, 0, 1/(zfar-znear), 0,
296             (left+right)/(left-right), (top+bottom)/(bottom-top), -znear/(znear-zfar), 1
297         ]);
298     }
299 
300     static Matrix4 alternateHandedness(Matrix4 mat)
301     {
302         return Matrix4([
303             1, 0, 0, 0,
304             0, 1, 0, 1,
305             0, 0,-1, 0,
306             0, 0, 0, 1
307         ]) * mat;
308     }
309 
310     alias values this;
311 }
312 
313 auto toString()(in Matrix3 mat) @nogc
314 {
315     import hip.util.conv;
316     import hip.util.string;
317     String ret = '[';
318     for(uint i = 0; i < 9; i++)
319     {
320         if(i != 0)
321         {
322             if(i%3 == 0)
323                 ret~= "]\n[";
324             else
325                 ret~=", ";
326         }
327         toStringRange(ret, mat[i]);
328     }
329     ret~= ']';
330     return ret;
331 }
332 
333 auto toString()(in Matrix4 mat) @nogc
334 {
335     import hip.util.conv;
336     import hip.util.string;
337     String ret = "[";
338 
339     for(uint i = 0; i < 16; i++)
340     {
341         if(i != 0)
342         {
343             if(i%4 == 0)
344                 ret~= "]\n[";
345             else
346                 ret~=", ";
347         }            
348         toStringRange(ret, mat[i]);
349     }
350     ret~= ']';
351     return ret;
352 }